#include "cmd_process.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "user_bt.h"
#include "user_camera.h"
#include "user_codec.h"
#include "user_fs.h"
#include "user_utils.h"
#include "user_wifi.h"
#include <stdbool.h>

#define TAG "CMD_PROCESS"

static void camera_handler(void);
static void ota_handler(void);
static void WiFi_handler(int count_100ms);
static void recording_handler(int count_100ms);
static void cmd_process_task(void *param);
static void BLE_audio_data_process(uint8_t *data, int data_len);
static void speaker_handler(int count_100ms);

typedef struct {
    struct {
        bool needOTA;
        char filePath[30];
    } ota_info;

    struct {
        int timeout;
        bool needUpdateConfig;
        wifi_action_t action;
    } wifi_info;

    struct {
        int timeout;
        bool needUpdateDiscovery;
        bool needReconnect;
    } bt_info;

    struct {
        int timeout;
        bool needUpdateConfig;
        bool needReconnect;
    } ble_info;

    struct {
        bool needUpdateConfig;
        media_state_t state;
        int timeout;
    } media_info;

    struct {
        bool needUpdateConfig;
        int timeout;
    } speaker_info;

    struct {
        bool needTakePic;
    } camera_info;
} cmd_process_info_t;
cmd_process_info_t cmd_process_info;

typedef struct {
    char *cmd;
    char *(*cmd_func)(cJSON *root);
} json_cmd_t;

static const json_cmd_t cmd_list[] = {
    {"queryDevInfo", getDevInfo},
    {"queryBaseInfo", getBaseInfo},
    {"queryWiFiInfo", getWiFiInfo},
    {"queryBTInfo", getBTInfo},
    {"queryBLEInfo", getBLEInfo},
    {"setWiFi", cmd_setWiFi},
    {"setBT", cmd_setBT},
    {"setBLE", cmd_setBLE},
    {"setVolume", cmd_setVol},
    {"takePic", cmd_takePic},
    {"setRecord", cmd_setRecord},
    {"setRecognize", cmd_setRecognize},
    {"setHttpServer", cmd_setHttpServer},
    {"ota", cmd_otaFirmware},
};

// 文件接收处理
void recv_file_handler(uint8_t *data, int data_len) {
    if (data_len < 7) {
        ESP_LOGE(TAG, "Invalid data length: %d", data_len);
        return;
    }
    if (((data[1] >> 3) & 0x02) == 2 && data_len > 7) { // OPUS
        // 音频流 opus格式
        ESP_LOGI(TAG, "Audio stream data: %d", data_len);
        BLE_audio_data_process(&data[7], data_len - 7);
    } else {
        ESP_LOGW(TAG, "Unknown data type: %d", data[1]);
    }
}

// json命令处理
char *cmd_handler(char *data, int data_len) {
    ESP_LOGI(TAG, "cmd_handler:%s", data);

    char *response = NULL;
    cJSON *root = cJSON_Parse(data);
    if (root == NULL) {
        ESP_LOGE(TAG, "cJSON_Parse failed: %s", data);
        return NULL;
    }

    cJSON *cmd = cJSON_GetObjectItem(root, "cmd");
    if (cmd == NULL || !cJSON_IsString(cmd)) {
        ESP_LOGE(TAG, "Invalid cmd format");
        cJSON_Delete(root);
        return NULL;
    }

    for (int i = 0; i < sizeof(cmd_list) / sizeof(json_cmd_t); i++) {
        if (strcmp(cmd->valuestring, cmd_list[i].cmd) == 0 && cmd_list[i].cmd_func != NULL) {
            ESP_LOGI(TAG, "cmd: %s", cmd->valuestring);
            response = cmd_list[i].cmd_func(root);
            break;
        }
    }

    cJSON_Delete(root);

    return response;
}

#define USER_CMD
#ifdef USER_CMD
// 设置WiFi信息
char *cmd_setWiFi(cJSON *root) {
    cJSON *wifi = cJSON_GetObjectItem(root, "data");
    if (wifi == NULL || !cJSON_IsObject(wifi)) {
        ESP_LOGE(TAG, "Invalid setWiFi format");
        return NULL;
    }
    cJSON *ssid = cJSON_GetObjectItem(wifi, "ssid");
    cJSON *password = cJSON_GetObjectItem(wifi, "pass");
    cJSON *reconnect = cJSON_GetObjectItem(wifi, "reconnect");
    if (ssid == NULL || password == NULL || reconnect == NULL) {
        ESP_LOGE(TAG, "Invalid setWiFi format");
        return NULL;
    }
    if (!cJSON_IsString(ssid) || !cJSON_IsString(password) || !cJSON_IsBool(reconnect)) {
        ESP_LOGE(TAG, "Invalid setWiFi format");
        return NULL;
    }
    if (strlen(ssid->valuestring) > 32 || strlen(password->valuestring) > 64) {
        ESP_LOGE(TAG, "Invalid ssid or password length");
        return NULL;
    }
    if (strcmp(ssid->valuestring, devInfo.wifiInfo.ssid) != 0 || strcmp(password->valuestring, devInfo.wifiInfo.password) != 0) {
        strcpy(devInfo.wifiInfo.ssid, ssid->valuestring);
        strcpy(devInfo.wifiInfo.password, password->valuestring);
        cmd_process_info.wifi_info.needUpdateConfig = true;
        nvs_write("wifi_ssid", devInfo.wifiInfo.ssid);
        nvs_write("wifi_pass", devInfo.wifiInfo.password);
        ESP_LOGI(TAG, "WiFi ssid:%s pass:%s", devInfo.wifiInfo.ssid, devInfo.wifiInfo.password);
    }
    if (reconnect->valueint) {
        cmd_process_info.wifi_info.action = WIFI_ACTION_RESTART;
    }
    return NULL;
}

// 设置BT
char *cmd_setBT(cJSON *root) {
    cJSON *data = cJSON_GetObjectItem(root, "data");
    if (data == NULL || !cJSON_IsObject(data)) {
        ESP_LOGE(TAG, "Invalid setBT format");
        return NULL;
    }
    cJSON *enable = cJSON_GetObjectItem(data, "enable");
    if (enable == NULL || !cJSON_IsBool(enable)) {
        ESP_LOGE(TAG, "Invalid enableBT format");
        return NULL;
    }
    devInfo.BTInfo.enable = enable->valueint;

    cJSON *timeout = cJSON_GetObjectItem(data, "timeout");
    if (timeout == NULL || !cJSON_IsNumber(timeout)) {
        ESP_LOGI(TAG, "not set timeout, set to 0");
        cmd_process_info.bt_info.timeout = 0;
        return NULL;
    }
    if (timeout->valueint) {
        cmd_process_info.bt_info.timeout = timeout->valueint;
        cmd_process_info.bt_info.needUpdateDiscovery = true;
    } else {
        cmd_process_info.bt_info.timeout = 0;
        cmd_process_info.bt_info.needUpdateDiscovery = false;
    }

    cJSON *reconnect = cJSON_GetObjectItem(data, "reconnect");
    if (reconnect == NULL || !cJSON_IsBool(reconnect)) {
        ESP_LOGE(TAG, "Invalid reconnect format");
        return NULL;
    }
    cmd_process_info.bt_info.needReconnect = reconnect->valueint;

    return NULL;
}

// 设置BLE
char *cmd_setBLE(cJSON *root) {
    cJSON *data = cJSON_GetObjectItem(root, "data");
    if (data == NULL || !cJSON_IsObject(data)) {
        ESP_LOGE(TAG, "Invalid setBT format");
        return NULL;
    }
    cJSON *enable = cJSON_GetObjectItem(data, "enable");
    if (enable == NULL || !cJSON_IsBool(enable)) {
        ESP_LOGE(TAG, "Invalid enableBT format");
        return NULL;
    }
    devInfo.BLEInfo.enable = enable->valueint;

    cJSON *timeout = cJSON_GetObjectItem(data, "timeout");
    if (timeout == NULL || !cJSON_IsNumber(timeout)) {
        ESP_LOGI(TAG, "not set timeout, set to 0");
        cmd_process_info.ble_info.timeout = 0;
        return NULL;
    }
    if (timeout->valueint) {
        cmd_process_info.ble_info.timeout = timeout->valueint;
    } else {
        cmd_process_info.ble_info.timeout = 0;
    }

    cJSON *mac = cJSON_GetObjectItem(data, "mac");
    if (mac == NULL || !cJSON_IsString(mac)) {
        ESP_LOGE(TAG, "Invalid mac format");
        return NULL;
    }
    if (strcmp(mac->valuestring, devInfo.BLEInfo.mac_str) != 0) {
        strcpy(devInfo.BLEInfo.mac_str, mac->valuestring);
        cmd_process_info.ble_info.needUpdateConfig = true;
        nvs_write("ble_mac", devInfo.BLEInfo.mac_str);
    }

    cJSON *reconnect = cJSON_GetObjectItem(data, "reconnect");
    if (reconnect == NULL || !cJSON_IsBool(reconnect)) {
        ESP_LOGE(TAG, "Invalid reconnect format");
        return NULL;
    }
    cmd_process_info.ble_info.needReconnect = reconnect->valueint;

    return NULL;
}

// 设置音量
char *cmd_setVol(cJSON *root) {
    cJSON *data = cJSON_GetObjectItem(root, "data");
    if (data == NULL || !cJSON_IsObject(data)) {
        ESP_LOGE(TAG, "Invalid setVolume format");
        return NULL;
    }
    cJSON *volume = cJSON_GetObjectItem(data, "vol");
    if (volume == NULL || !cJSON_IsNumber(volume)) {
        ESP_LOGE(TAG, "Invalid setVolume format");
        return NULL;
    }
    if (volume->valueint < 0 || volume->valueint > 100) {
        ESP_LOGE(TAG, "Invalid volume value");
        return NULL;
    }
    devInfo.mediaInfo.volume = volume->valueint;
    nvs_write("volume", devInfo.BLEInfo.mac_str);
    user_codec_set_volume(devInfo.mediaInfo.volume);
    return NULL;
}

// 设置拍照
char *cmd_takePic(cJSON *root) {
    cmd_process_info.camera_info.needTakePic = true;
    return NULL;
}

// 设置录像
char *cmd_setRecord(cJSON *root) {
    cJSON *data = cJSON_GetObjectItem(root, "data");
    if (data == NULL || !cJSON_IsObject(data)) {
        ESP_LOGE(TAG, "Invalid record format");
        return NULL;
    }
    cJSON *enable = cJSON_GetObjectItem(data, "enable");
    if (enable == NULL || !cJSON_IsBool(enable)) {
        ESP_LOGE(TAG, "Invalid enableRecord format");
        return NULL;
    }
    cJSON *timeout = cJSON_GetObjectItem(data, "timeout");
    if (timeout == NULL || !cJSON_IsNumber(timeout)) {
        ESP_LOGI(TAG, "not set timeout, set to 0");
        cmd_process_info.media_info.state = media_IDLE;
        cmd_process_info.media_info.timeout = 0;
        cmd_process_info.media_info.needUpdateConfig = true;
        return NULL;
    }
    if (enable->valueint && timeout->valueint) {
        cmd_process_info.media_info.state = media_recording;
        cmd_process_info.media_info.timeout = timeout->valueint;
    } else {
        cmd_process_info.media_info.state = media_IDLE;
        cmd_process_info.media_info.timeout = 0;
    }
    cmd_process_info.media_info.needUpdateConfig = true;

    return NULL;
}

// 设置识别
char *cmd_setRecognize(cJSON *root) {
    cJSON *data = cJSON_GetObjectItem(root, "data");
    if (data == NULL || !cJSON_IsObject(data)) {
        ESP_LOGE(TAG, "Invalid record format");
        return NULL;
    }
    cJSON *enable = cJSON_GetObjectItem(data, "enable");
    if (enable == NULL || !cJSON_IsBool(enable)) {
        ESP_LOGE(TAG, "Invalid enableRecord format");
        return NULL;
    }
    cJSON *timeout = cJSON_GetObjectItem(data, "timeout");
    if (timeout == NULL || !cJSON_IsNumber(timeout)) {
        ESP_LOGI(TAG, "not set timeout, set to 0");
        cmd_process_info.media_info.state = media_IDLE;
        cmd_process_info.media_info.timeout = 0;
        cmd_process_info.media_info.needUpdateConfig = true;
        return NULL;
    }
    if (enable->valueint && timeout->valueint) {
        cmd_process_info.media_info.state = media_recognize;
        cmd_process_info.media_info.timeout = timeout->valueint;
    } else {
        cmd_process_info.media_info.state = media_IDLE;
    }
    cmd_process_info.media_info.needUpdateConfig = true;

    return NULL;
}

// 使能http server
char *cmd_setHttpServer(cJSON *root) {
    cJSON *data = cJSON_GetObjectItem(root, "data");
    if (data == NULL || !cJSON_IsObject(data)) {
        ESP_LOGE(TAG, "Invalid httpServer format");
        return NULL;
    }
    cJSON *enable = cJSON_GetObjectItem(data, "enable");
    if (enable == NULL || !cJSON_IsBool(enable)) {
        ESP_LOGE(TAG, "Invalid enableHttpServer format");
        return NULL;
    }
    cJSON *timeout = cJSON_GetObjectItem(data, "timeout");
    if (timeout == NULL || !cJSON_IsNumber(timeout)) {
        ESP_LOGI(TAG, "not set timeout, set to 0");
        devInfo.wifiInfo.httpServer = false;
        cmd_process_info.wifi_info.timeout = 0;
        cmd_process_info.wifi_info.action = WIFI_ACTION_STOP;
        return NULL;
    }
    if (enable->valueint && timeout->valueint) {
        devInfo.wifiInfo.httpServer = true;
        cmd_process_info.wifi_info.timeout = timeout->valueint;
        cmd_process_info.wifi_info.action = WIFI_ACTION_START;
    } else {
        devInfo.wifiInfo.httpServer = false;
        cmd_process_info.wifi_info.timeout = 0;
        cmd_process_info.wifi_info.action = WIFI_ACTION_STOP;
    }

    return NULL;
}

// 设备升级
char *cmd_otaFirmware(cJSON *root) {
    cJSON *data, *filepath;
    data = cJSON_GetObjectItem(root, "data");
    if (data == NULL || !cJSON_IsObject(data)) {
        ESP_LOGE(TAG, "Invalid ota format");
        return NULL;
    }

    filepath = cJSON_GetObjectItem(data, "filepath");
    if (filepath == NULL || !cJSON_IsString(filepath)) {
        ESP_LOGE(TAG, "Invalid filepath format");
        return NULL;
    }

    if (strlen(filepath->valuestring) > 10) {
        ESP_LOGE(TAG, "Invalid filepath length");
        return NULL;
    }

    snprintf(cmd_process_info.ota_info.filePath, sizeof(cmd_process_info.ota_info.filePath), "%s/%s", POINT_PATH, filepath->valuestring);
    cmd_process_info.ota_info.needOTA = true;

    return NULL;
}

// 获取设备信息
char *getDevInfo(cJSON *root) {
    char *response_str = NULL;
    cJSON *response, *data;
    response = cJSON_CreateObject();
    if (response == NULL) {
        ESP_LOGE(TAG, "cJSON_CreateObject failed");
        return NULL;
    }
    cJSON_AddStringToObject(response, "cmd", "devInfo");

    data = cJSON_CreateObject();
    if (data == NULL) {
        ESP_LOGE(TAG, "cJSON_CreateObject failed");
        cJSON_Delete(response);
        return NULL;
    }
    cJSON_AddItemToObject(response, "data", data);
    // Add base information to the response
    cJSON_AddStringToObject(data, "sn", devInfo.sn);
    cJSON_AddStringToObject(data, "shortSn", devInfo.SAddr_str);
    cJSON_AddStringToObject(data, "BLEName", devInfo.deviceName);
    cJSON_AddStringToObject(data, "softVer", SOFT_VER);
    cJSON_AddStringToObject(data, "hardVer", HARD_VER);
    cJSON_AddStringToObject(data, "isRelease", IS_RELEASE);
    // Convert the response to a string and send it back
    response_str = cJSON_PrintUnformatted(response);
    if (response_str != NULL) {
        ESP_LOGI(TAG, "Response: %s", response_str);
    } else {
        ESP_LOGE(TAG, "cJSON_PrintUnformatted failed");
    }

    cJSON_Delete(response);

    return response_str;
}

// 获取基础信息
char *getBaseInfo(cJSON *root) {
    char *response_str = NULL;
    cJSON *response, *data;
    response = cJSON_CreateObject();
    if (response == NULL) {
        ESP_LOGE(TAG, "cJSON_CreateObject failed");
        return NULL;
    }
    cJSON_AddStringToObject(response, "cmd", "baseInfo");

    data = cJSON_CreateObject();
    if (data == NULL) {
        ESP_LOGE(TAG, "cJSON_CreateObject failed");
        cJSON_Delete(response);
        return NULL;
    }
    cJSON_AddItemToObject(response, "data", data);
    cJSON_AddNumberToObject(data, "vol", devInfo.mediaInfo.volume);
    cJSON_AddNumberToObject(data, "bat", devInfo.bat);

    char *update_str = cJSON_PrintUnformatted(response);
    if (update_str != NULL) {
        ESP_LOGI(TAG, "Response len:%d data:%s", strlen(update_str), update_str);
    } else {
        ESP_LOGE(TAG, "cJSON_PrintUnformatted failed");
    }

    cJSON_Delete(response);

    return update_str;
}

// 获取WiFi信息
char *getWiFiInfo(cJSON *root) {
    char *response_str = NULL;
    cJSON *response, *data;
    response = cJSON_CreateObject();
    if (response == NULL) {
        ESP_LOGE(TAG, "cJSON_CreateObject failed");
        return NULL;
    }
    cJSON_AddStringToObject(response, "cmd", "WiFiInfo");

    data = cJSON_CreateObject();
    if (data == NULL) {
        ESP_LOGE(TAG, "cJSON_CreateObject failed");
        cJSON_Delete(response);
        return NULL;
    }
    cJSON_AddItemToObject(response, "data", data);
    cJSON_AddStringToObject(data, "ssid", devInfo.wifiInfo.ssid);
    cJSON_AddStringToObject(data, "pass", devInfo.wifiInfo.password);
    cJSON_AddBoolToObject(data, "enable", devInfo.wifiInfo.enable);
    cJSON_AddBoolToObject(data, "conn", devInfo.wifiInfo.connState);
    cJSON_AddBoolToObject(data, "http", devInfo.wifiInfo.httpServer);
    cJSON_AddStringToObject(data, "ip", get_ip_address());

    // Convert the response to a string and send it back
    response_str = cJSON_PrintUnformatted(response);
    if (response_str != NULL) {
        ESP_LOGI(TAG, "Response: %s", response_str);
    } else {
        ESP_LOGE(TAG, "cJSON_PrintUnformatted failed");
    }

    cJSON_Delete(response);

    return response_str;
}

// 获取BT信息
char *getBTInfo(cJSON *root) {
    char *response_str = NULL;
    cJSON *response, *data;
    response = cJSON_CreateObject();
    if (response == NULL) {
        ESP_LOGE(TAG, "cJSON_CreateObject failed");
        return NULL;
    }
    cJSON_AddStringToObject(response, "cmd", "BTInfo");

    data = cJSON_CreateObject();
    if (data == NULL) {
        ESP_LOGE(TAG, "cJSON_CreateObject failed");
        cJSON_Delete(response);
        return NULL;
    }
    cJSON_AddItemToObject(response, "data", data);
    cJSON_AddBoolToObject(data, "enable", devInfo.BTInfo.enable);
    cJSON_AddBoolToObject(data, "conn", devInfo.BTInfo.connState);
    cJSON_AddStringToObject(data, "mac", devInfo.BTInfo.mac_str);
    // Convert the response to a string and send it back
    response_str = cJSON_PrintUnformatted(response);
    if (response_str != NULL) {
        ESP_LOGI(TAG, "Response: %s", response_str);
    } else {
        ESP_LOGE(TAG, "cJSON_PrintUnformatted failed");
    }

    cJSON_Delete(response);

    return response_str;
}

// 获取BLE信息
char *getBLEInfo(cJSON *root) {
    char *response_str = NULL;
    cJSON *response, *data;
    response = cJSON_CreateObject();
    if (response == NULL) {
        ESP_LOGE(TAG, "cJSON_CreateObject failed");
        return NULL;
    }
    cJSON_AddStringToObject(response, "cmd", "BTInfo");

    data = cJSON_CreateObject();
    if (data == NULL) {
        ESP_LOGE(TAG, "cJSON_CreateObject failed");
        cJSON_Delete(response);
        return NULL;
    }
    cJSON_AddItemToObject(response, "data", data);
    cJSON_AddBoolToObject(data, "enable", devInfo.BLEInfo.enable);
    cJSON_AddBoolToObject(data, "conn", devInfo.BLEInfo.connState);
    cJSON_AddStringToObject(data, "mac", devInfo.BLEInfo.mac_str);
    // Convert the response to a string and send it back
    response_str = cJSON_PrintUnformatted(response);
    if (response_str != NULL) {
        ESP_LOGI(TAG, "Response: %s", response_str);
    } else {
        ESP_LOGE(TAG, "cJSON_PrintUnformatted failed");
    }

    cJSON_Delete(response);

    return response_str;
}

#endif // USER_CMD

// 创建命令处理线程
void cmd_process_init(void) {
    camera_handler();
    ota_handler();
    WiFi_handler(0);
    recording_handler(0);
    speaker_handler(0);
    xTaskCreate(cmd_process_task, "cmd_process_task", 4096, NULL, 5, NULL);
    ESP_LOGI(TAG, "cmd_process_task created");
}

// 命令处理线程
void cmd_process_task(void *param) {
    static int count_100ms = 0;
    while (1) {
        camera_handler();
        ota_handler();
        WiFi_handler(count_100ms);
        recording_handler(count_100ms);
        speaker_handler(count_100ms);
        count_100ms++;
        vTaskDelay(pdMS_TO_TICKS(100)); // 100ms轮询
    }

    vTaskDelete(NULL); // 永远不会到达这里
}

static void camera_handler(void) {
    static int pic_count = 0;
    if (cmd_process_info.camera_info.needTakePic) {
        cmd_process_info.camera_info.needTakePic = false;
        char filepath[40];
        pic_count++;
        snprintf(filepath, sizeof(filepath), "%s/rgb565_%d.raw", POINT_PATH, pic_count);
        camera_capture(filepath);
    }
}

static void ota_handler(void) {
    if (cmd_process_info.ota_info.needOTA) {
        cmd_process_info.ota_info.needOTA = false;
        update_firmware(cmd_process_info.ota_info.filePath);
    }
}

static void WiFi_handler(int count_100ms) {
    static bool init_flag = false;
    if (!init_flag) {
        user_wifi_updateConfig(devInfo.wifiInfo.ssid, devInfo.wifiInfo.password);
        cmd_process_info.wifi_info.timeout = 999;
        cmd_process_info.wifi_info.action = WIFI_ACTION_START;
        init_flag = true;
    }
    if (cmd_process_info.wifi_info.needUpdateConfig == true) {
        user_wifi_updateConfig(devInfo.wifiInfo.ssid, devInfo.wifiInfo.password);
        cmd_process_info.wifi_info.needUpdateConfig = false;
    }

    if (cmd_process_info.wifi_info.action != WIFI_ACTION_NONE) {
        user_set_wifi_state(cmd_process_info.wifi_info.action);
        cmd_process_info.wifi_info.action = WIFI_ACTION_NONE;
    }
    if (count_100ms % 10 == 0) {
        if (cmd_process_info.wifi_info.timeout > 0) {
            cmd_process_info.wifi_info.timeout--;
            if (cmd_process_info.wifi_info.timeout == 0) {
                user_set_wifi_state(WIFI_ACTION_STOP);
                ESP_LOGI(TAG, "wifi auto down");
            }
        }
    }
}

#define BLE_AUDIO_RECORDER
#ifdef BLE_AUDIO_RECORDER
typedef struct {
    uint8_t fileID;
    union {
        struct {
            uint8_t is_first_frame : 1; // bit 0
            uint8_t is_last_frame : 1;  // bit 1
            uint8_t is_stream : 1;      // bit 2
            uint8_t file_type : 4;      // bit 3~6
            uint8_t reserved : 1;       // bit 7
        };
        uint8_t value; // 作为 1 字节使用
    } bits;
    uint16_t packetID;
} file_header_t;

file_header_t record_header = {.fileID = 0x10,
                               .bits = {.is_first_frame = 0,
                                        .is_last_frame = 0,
                                        .is_stream = 1,
                                        .file_type = 2, // OPUS
                                        .reserved = 0},
                               .packetID = 0};

// 音频数据处理函数
static void record_data_handler(audio_element_state_t state, uint8_t *data, int len) {
    static uint8_t tempBuff[180];
    ESP_LOGI(TAG, "state:%02x, len:%d", state, len);
    if (len > 160) {
        ESP_LOGE(TAG, "data len error:%d", len);
        return;
    }
    if (len < 0) {
        len = 0; // 小于0为结束相关标志
    }
    if (state == AEL_STATE_INITIALIZING) { // audio pipeline初始化,准备传输音频流
        record_header.bits.is_first_frame = true;
        record_header.bits.is_last_frame = false;

        record_header.fileID++;
        tempBuff[0] = record_header.fileID;
        tempBuff[1] = record_header.bits.value;
        tempBuff[2] = '\0'; // 保留
        tempBuff[3] = 0x00; // 流数据无size
        tempBuff[4] = 0x00; // size
        tempBuff[5] = 0x00; // size
        tempBuff[6] = 0x00; // size
        tempBuff[7] = 0x00; // 流数据无PACKET NUM
        tempBuff[8] = 0x00; // PACKET NUM
        tempBuff[9] = 0x00; // PAYLOAD LEN
        tempBuff[10] = 160;
        record_header.packetID = 0;
        strcpy((char *)&tempBuff[11], "record.opus");
        ble_sendFile_notify((char *)tempBuff, 22);
    } else {
        if (state == AEL_STATE_RUNNING) { // 普通包
            record_header.bits.is_first_frame = false;
            record_header.bits.is_last_frame = false;
        } else if (state == AEL_STATE_PAUSED || state == AEL_STATE_STOPPED || state == AEL_STATE_FINISHED) {
            ESP_LOGI(TAG, "record stop"); // 结束包
            record_header.bits.is_first_frame = false;
            record_header.bits.is_last_frame = true;
        } else { // 未知错误
            ESP_LOGE(TAG, "record error");
            record_header.bits.is_first_frame = false;
            record_header.bits.is_last_frame = true;
        }
        tempBuff[0] = record_header.fileID;
        tempBuff[1] = record_header.bits.value;
        tempBuff[2] = 0;                             // 保留
        tempBuff[3] = record_header.packetID >> 8;   // size
        tempBuff[4] = record_header.packetID & 0xFF; // size
        tempBuff[5] = 0;
        tempBuff[6] = len;
        if (len > 0)
            memcpy(&tempBuff[7], data, len);
        ble_sendFile_notify((char *)tempBuff, len + 7);
    }
    record_header.packetID++;
    vTaskDelay(20 / portTICK_PERIOD_MS); // 20ms延时,避免数据包过快
}

static void recording_handler(int count_100ms) {
    static bool init_flag = false;
    if (!init_flag) {
        record_regist_handler(record_data_handler);
        // cmd_process_info.media_info.timeout = 999;
        init_flag = true;
    }
    if (cmd_process_info.media_info.needUpdateConfig) {
        if (cmd_process_info.media_info.state != media_IDLE && cmd_process_info.media_info.timeout) {
            record_data_handler(AEL_STATE_INITIALIZING, NULL, 0);
            record_reconfigure((audio_sink_config_t){
                .enable = true,
                .source = AUDIO_SINK_BLE,
                .data = NULL,
            });
        } else {
            record_reconfigure((audio_sink_config_t){
                .enable = false,
                .source = AUDIO_SINK_BLE,
                .data = NULL,
            });
            record_data_handler(AEL_STATE_STOPPED, NULL, 0);
        }
        cmd_process_info.media_info.needUpdateConfig = false;
    }
    if (count_100ms % 10 == 0) {
        if (cmd_process_info.media_info.timeout > 0) {
            cmd_process_info.media_info.timeout--;
            if (cmd_process_info.media_info.timeout == 0) {
                cmd_process_info.media_info.state = media_IDLE;
                record_reconfigure((audio_sink_config_t){
                    .enable = false,
                    .source = AUDIO_SINK_BLE,
                    .data = NULL,
                });
                record_data_handler(AEL_STATE_STOPPED, NULL, 0);
            }
        }
    }
}
#endif

#define BLE_AUDIO_PLAYER
#ifdef BLE_AUDIO_PLAYER

#define BLE_AUDIO_TIMEOUT 3
EXT_RAM_BSS_ATTR static uint8_t ble_audio_buffer[BLE_AUDIO_BUFFER_SIZE] = {0};
static ble_ringbuffer_t ble_ringbuffer = {.buffer = (uint8_t *)ble_audio_buffer, .write_index = 0, .size = 0}; // 初始化环形缓冲区

// 写入缓冲区
static void BLE_audio_data_process(uint8_t *data, int data_len) {
    if (data_len > BLE_PACKET_MAX_LEN || data_len < 0) {
        ESP_LOGE(TAG, "data len error:%d", data_len);
        return;
    }
    if (data_len < 0) {
        data_len = 0; // 小于0为结束相关标志
    }
    if (ble_ringbuffer.size + data_len > BLE_AUDIO_BUFFER_SIZE - BLE_PACKET_MAX_LEN) {
        ble_ringbuffer.size = BLE_AUDIO_BUFFER_SIZE - BLE_PACKET_MAX_LEN;
        ESP_LOGW(TAG, "buffer overflow, overlay data:%d", ble_ringbuffer.size + data_len - BLE_AUDIO_BUFFER_SIZE);
    } else {
        ble_ringbuffer.size += data_len;
    }
    if (ble_ringbuffer.write_index + data_len > BLE_AUDIO_BUFFER_SIZE) {
        int len = BLE_AUDIO_BUFFER_SIZE - ble_ringbuffer.write_index;
        memcpy(&ble_ringbuffer.buffer[ble_ringbuffer.write_index], data, len);
        memcpy(ble_ringbuffer.buffer, &data[len], data_len - len);
    } else {
        memcpy(&ble_ringbuffer.buffer[ble_ringbuffer.write_index], data, data_len);
    }
    ble_ringbuffer.write_index += data_len;
    ble_ringbuffer.write_index %= BLE_AUDIO_BUFFER_SIZE;
    ESP_LOGI(TAG, "write index:%d, size:%d", ble_ringbuffer.write_index, ble_ringbuffer.size);
}

// 读取缓冲区
static int BLE_audio_read_data(uint8_t *data, int data_len) {
    int timeout = BLE_AUDIO_TIMEOUT;
    while (timeout -= 100) {
        if (ble_ringbuffer.size > 0) {
            break;
        }
        vTaskDelay(100 / portTICK_PERIOD_MS); // 等待数据到来
    }
    if (ble_ringbuffer.size == 0) {
        ESP_LOGW(TAG, "ble ringbuffer empty and timeout");
        return data_len;
    }

    if (ble_ringbuffer.size < data_len) {
        data_len = ble_ringbuffer.size;
    }

    // 读取数据
    int data_start = (ble_ringbuffer.write_index + BLE_AUDIO_BUFFER_SIZE - ble_ringbuffer.size) % BLE_AUDIO_BUFFER_SIZE;
    ESP_LOGI(TAG, "read index:%d, size:%d", data_start, ble_ringbuffer.size);
    if (data_start + data_len > BLE_AUDIO_BUFFER_SIZE) {
        int len = BLE_AUDIO_BUFFER_SIZE - data_start;
        memcpy(data, &ble_ringbuffer.buffer[data_start], len);
        memcpy(&data[len], ble_ringbuffer.buffer, data_len - len);
    } else {
        memcpy(data, &ble_ringbuffer.buffer[data_start], data_len);
    }
    ble_ringbuffer.size -= data_len;

    return data_len;
}

// 本地音频播放
static FILE *ble_reader = NULL;
void local_audio_setPath(char *path) {
    if (ble_reader != NULL) {
        fclose(ble_reader);
        ble_reader = NULL;
    }
    ble_reader = fopen(path, "rb");
    if (ble_reader == NULL) {
        ESP_LOGE(TAG, "open ble_audio failed");
    } else {
        ESP_LOGI(TAG, "open ble_audio success");
    }
}

// 读取本地音频数据
static int local_audio_read_data(uint8_t *data, int data_len) {
    if (ble_reader == NULL) {
        ESP_LOGE(TAG, "ble_reader is NULL");
        return -1;
    }
    ssize_t len = fread(data, 1, data_len, ble_reader);
    if (len >= 0) {
        if (len < data_len) {
            ESP_LOGI(TAG, "read data end, len:%d", len);
            fclose(ble_reader);
            ble_reader = NULL;
        } else {
            ESP_LOGI(TAG, "read data len:%d", len);
        }
        return len;
    } else {
        ESP_LOGE(TAG, "read data error:%d", len);
        fclose(ble_reader);
        ble_reader = NULL;
        return -1;
    }
}

// 音频流数据处理函数
static int speaker_data_in_handler(uint8_t *data, int len) {
    if (len <= 0) {
        return 0;
    }

    if (ble_reader != NULL) {
        // ESP_LOGI(TAG, "speaker data from local audio file, len:%d", len);
        return local_audio_read_data(data, len);
    } else {
        // ESP_LOGI(TAG, "speaker data from ble ringbuffer, len:%d", len);
        return BLE_audio_read_data(data, len);
    }
}

// 播放处理
static void speaker_handler(int count_100ms) {
    static bool init_flag = false;
    if (!init_flag) {
        userDataIn_regist_handler(speaker_data_in_handler);
        init_flag = true;
        // local_audio_setPath("/littlefs/input.opus"); // 待修改为设备启动音
    }
    if ((ble_ringbuffer.size || ble_reader) && cmd_process_info.speaker_info.timeout == 0) {
        // ble ringbuffer或者reader任意一个有数据
        ESP_LOGI(TAG, "enable BLE audio pipeline");
        audio_source_config_t config = {
            .enable = true,
            .source = AUDIO_SOURCE_BLE,
        };
        player_reconfigure(config);
        cmd_process_info.speaker_info.timeout = 30; // 等待缓冲区播放完毕
    }
    if (ble_ringbuffer.size == 0 && ble_reader == NULL && cmd_process_info.speaker_info.timeout > 0) {
        cmd_process_info.speaker_info.timeout--;
        if (cmd_process_info.speaker_info.timeout == 0) {
            ESP_LOGI(TAG, "disable BLE audio pipeline");
            audio_source_config_t config = {
                .enable = false,
                .source = AUDIO_SOURCE_BLE,
            };
            player_reconfigure(config);
        }
    }
}

#endif

static void bt_handler() {
    if (cmd_process_info.bt_info.needReconnect) {
        cmd_process_info.bt_info.needReconnect = false;
        user_bt_connect(true, NULL);
    }
    if (cmd_process_info.bt_info.needUpdateDiscovery) {
        cmd_process_info.bt_info.needUpdateDiscovery = false;
        user_bt_discovery_config(cmd_process_info.bt_info.needUpdateDiscovery);
    }
    if (cmd_process_info.bt_info.timeout > 0) {
        cmd_process_info.bt_info.timeout--;
        if (cmd_process_info.bt_info.timeout == 0) {
            user_bt_discovery_config(false);
        }
    }
}